package top.jacktgq.custom_controls.window;

import javafx.geometry.Bounds;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Region;
import javafx.stage.Window;

public class CandyWindowDragSupport {
    Window stage;
    Region root;
    Node anchor; // 暂未用到

    // 外部设定: 指定是否可以改变大小
    public boolean resizable = true;

    // 鼠标按下时、记下几组初始数据
    private boolean dragging = false;
    private boolean resizingH = false; // 水平方向可改变大小
    private boolean resizingV = false; // 竖直方向可改变大小
    private boolean moving = false; // 移动窗口位置

    private double windowX = 0, windowY = 0; // 初始窗口坐标
    private double windowW = 0, windowH = 0; // 初始窗口大小
    private double startX, startY; // 鼠标按下位置的相对坐标
    private double startScreenX, startScreenY; // 鼠标相对屏幕Screen坐标

    public CandyWindowDragSupport(Window stage, Region root, boolean resizable) {
        this(stage, root, null, resizable);
    }

    public CandyWindowDragSupport(Window stage, Region root, Node anchor, boolean resizable) {
        if (anchor == null)
            anchor = root;
        this.stage = stage;
        this.root = root;
        this.anchor = anchor;
        initDragSupport(resizable);
    }

    // 判断鼠标位置是否在边框上
    public boolean isResizingH(double x) {
        double w = root.getWidth();
        return x >= w - 4 && x < w;
    }

    public boolean isResizingV(double y) {
        double h = root.getHeight();
        return y >= h - 4 && y < h;
    }

    public boolean pointInAnchor(double screenX, double screenY) {
        Bounds bounds = anchor.localToScreen(anchor.getLayoutBounds());
        return bounds.contains(screenX, screenY);
    }

    public void initDragSupport(boolean resizable) {
        this.resizable = resizable;

        // root.addEventFilter(MouseEvent.MOUSE_MOVED, (e)->{
        root.setOnMouseMoved((e) -> {
            if (!resizable)
                return; // 如果设置为不可改变大小, 则不更改鼠标形状

            boolean rh = isResizingH(e.getX());
            boolean rv = isResizingV(e.getY());
            if (rh && rv)
                root.setCursor(Cursor.SE_RESIZE); // 水平 & 竖直
            else if (rh)
                root.setCursor(Cursor.H_RESIZE); // 仅水平改变大小
            else if (rv)
                root.setCursor(Cursor.V_RESIZE); // 仅竖直改变大小
            else
                root.setCursor(Cursor.DEFAULT);
        });

        // root.addEventFilter(MouseEvent.MOUSE_PRESSED, (e)->{
        root.setOnMousePressed((e) -> {

            // 记录鼠标点下时，窗口的坐标、大小
            windowX = stage.getX();
            windowY = stage.getY();
            windowW = stage.getWidth();
            windowH = stage.getHeight();

            // 记录鼠标点下时，鼠标的位置
            startX = e.getX();
            startY = e.getY();
            startScreenX = e.getScreenX();
            startScreenY = e.getScreenY();
        });

        // root.addEventFilter(MouseEvent.DRAG_DETECTED, (e)->{
        root.setOnDragDetected((e) -> {
            if (e.getButton() == MouseButton.PRIMARY) {
                dragging = true;

                // 判断用户的意图是要移动、还是改变大小
                resizingH = isResizingH(startX);
                resizingV = isResizingV(startY);
                if (!resizingH && !resizingV) {
                    if (pointInAnchor(startScreenX, startScreenY)) {
                        moving = true;
                    }
                }
            }
        });

        // root.addEventFilter(MouseEvent.MOUSE_DRAGGED, (e)->{
        root.setOnMouseDragged((e) -> {
            if (!dragging)
                return;

            // 计算鼠标的位移: 起始点A点，当前位置 B点
            double dx = e.getScreenX() - startScreenX;
            double dy = e.getScreenY() - startScreenY;

            // 移动窗口位置
            if (moving) {
                stage.setX(windowX + dx);
                stage.setY(windowY + dy);
                // System.out.println("moving...");
            }

            // 如果设为不可改变大小，则直接返回
            if (!resizable)
                return;

            // 改变窗口大小
            if (resizingH || resizingV) {
                double newW = windowW + dx;
                double newH = windowH + dy;
                // System.out.println("change size to: " + newW + "," + newH);
                if (resizingH)
                    root.setPrefWidth(newW);
                if (resizingV)
                    root.setPrefHeight(newH);
                // AfStyleWindow.this.setWidth(newW);
                // AfStyleWindow.this.setWidth(newH);
                stage.sizeToScene();
            }
        });

        // root.addEventFilter(MouseEvent.MOUSE_RELEASED, (e)->{
        root.setOnMouseReleased((e) -> {
            dragging = false;
            moving = false;
            resizingH = resizingV = false;
            root.setCursor(Cursor.DEFAULT);
        });
    }

    /**
     * 封装一个静态方法给外部调用
     * 
     * @param stage
     *            需要添加拖拽支持的无边框和标题栏样式的窗口
     * @param root
     *            窗口中的根容器
     * @param anchor
     *            窗口中的子容器或者控件：可以只给子容器或者控件添加拖拽支持，通常给自定义的标题栏添加拖拽支持，而其他位置无法拖拽，如果为空，那么默认窗口的任何位置都可以拖拽
     * @param resizable
     *            窗口是否支持缩放
     */
    public static void addDragSupport(Window stage, Region root, Node anchor, boolean resizable) {
        new CandyWindowDragSupport(stage, root, anchor, resizable);
    }

    /**
     * 封装一个静态方法给外部调用
     * 
     * @param stage
     *            需要添加拖拽支持的无边框和标题栏样式的窗口
     * @param root
     *            窗口中的根容器，窗口整体都支持拖拽
     * @param resizable
     *            窗口是否支持缩放
     */
    public static void addDragSupport(Window stage, Region root, boolean resizable) {
        addDragSupport(stage, root, null, resizable);
    }

}
